package io.persimmon.core.module.schedule;

import io.netty.util.HashedWheelTimer;
import io.persimmon.core.module.collection.AtomicIntegerMap;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;

@Data
@Slf4j
public class SingleScheduleImpl implements ScheduleTask {
    private static final AtomicIntegerMap<String> threadNoMap = new AtomicIntegerMap<>();
    private HashedWheelTimer hashedWheelTimer;
    private String name;
    private Long tickDurationMill;
    private Integer ticksPerWheel;

    public SingleScheduleImpl(String name) {
        this(name, 50L, 512);
    }

    public SingleScheduleImpl(String name, Long tickDurationMill, Integer ticksPerWheel) {
        tickDurationMill = Optional.ofNullable(tickDurationMill).orElse(50L);
        ticksPerWheel = Optional.ofNullable(ticksPerWheel).orElse(512);

        Integer incr = threadNoMap.incr(name, 1);
        String finalName = "hashed_wheel_timer" + ":" + name + "#" + incr;
        ThreadFactory threadFactory = new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName(finalName);
                return thread;
            }
        };

        this.name = finalName;
        this.tickDurationMill = tickDurationMill;
        this.ticksPerWheel = ticksPerWheel;
        this.hashedWheelTimer = new HashedWheelTimer(
                threadFactory,
                tickDurationMill,              // 每个槽位的时间间隔（tickDuration）
                TimeUnit.MILLISECONDS, // 时间单位
                ticksPerWheel             // 槽位数量（wheelSize，建议为2的幂次方，提升哈希效率）
        );
    }

    @Override
    public void submitDelayTask(Duration duration, Runnable runnable) {
        this.hashedWheelTimer.newTimeout(timeout -> runnable.run(),
                duration.toMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public void submitTimingTask(Duration duration, Callable<Duration> runnable) {
        this.hashedWheelTimer.newTimeout(timeout -> {
            Duration call = runnable.call();
            if (Objects.nonNull(call)) {
                submitTimingTask(call, runnable);
            }
        }, duration.toMillis(), TimeUnit.MILLISECONDS);
    }

    @Override
    public void stop() {
        this.hashedWheelTimer.stop();
        log.info("stop scheduleTask: {}", name);
    }
}
